package admin

import (
	"HeadLineNews/pkg/db/store"
	"HeadLineNews/pkg/snow"
	"HeadLineNews/pkg/zaplog"
	models "HeadLineNews/proto_models/manage/admin_model"
	"HeadLineNews/resp"
	"HeadLineNews/utils/gin_ctx"
	"HeadLineNews/utils/transaction"
	"database/sql"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/jmoiron/sqlx"
)

var (
	errAdminNameHasExists = errors.New("该用户已存在")
	errAdminUserNotExists = errors.New("用户名或密码错误")
)

// Repo service 与 MySQL 之间的交互
type Repo interface {
	CheckUniqueAdminName(c *gin.Context, adminName string) (err error)
	InsertAdminUser(c *gin.Context, p *models.RequestAdminRegister) (userId, token string, err error)
	CheckLoginData(c *gin.Context, p *models.RequestAdminLogin) (np *models.ResponseAdminLoginRegister, err error)
	ListAdmin(c *gin.Context, p *models.RequestListAdmin) (data *models.ResponseListAdmin, err error)
}

type repo struct {
	db *sqlx.DB
}

func NewRepo() Repo {
	return &repo{db: store.DB()}
}

// CheckUniqueAdminName 检查重复的用户名
func (r *repo) CheckUniqueAdminName(c *gin.Context, adminName string) (err error) {
	sqlStr := `SELECT COUNT(*) FROM manage__admin WHERE admin_name=?`
	var count int64
	if err = r.db.Get(&count, sqlStr, adminName); err != nil {
		zaplog.L().Error("query admin count by name failed", zaplog.Fields(c, adminName, err)...)
		resp.ServerBusy(c)
		return
	}
	if count > 0 {
		err = errAdminNameHasExists
		zaplog.L().Warn("admin name has exists", zaplog.Fields(c, adminName, nil)...)
		resp.UnprovedParam(c, err.Error())
	}
	return
}

// InsertAdminUser 将管理员用户名密码保存进数据库
func (r *repo) InsertAdminUser(c *gin.Context, p *models.RequestAdminRegister) (adminId, token string, err error) {
	if err = transaction.Transaction(r.db, func(tx *sqlx.Tx) (te error) {
		// 1.保存用户名密码
		sqlStr := `INSERT INTO manage__admin(
        admin_id, admin_name, admin_pwd, create_by, last_update_by, create_time, last_update_time) 
    VALUES(?, ?, ?, ?, ?, NOW(), NOW())`
		adminId = snow.GetID()
		if _, te = tx.Exec(sqlStr, adminId, p.AdminName, p.AdminPwd,
			c.GetString(gin_ctx.UserId), c.GetString(gin_ctx.UserId)); te != nil {
			zaplog.L().Error("insert admin user failed", zaplog.Fields(c, p, te)...)
			return
		}
		// 2.签发 token
		if token, te = NewCache().RegAdminRegisterToken(adminId); te != nil {
			zaplog.L().Error("reg token failed", zaplog.Fields(c, adminId, te)...)
			return
		}
		return
	}); err != nil {
		resp.ServerBusy(c)
		return
	}
	return
}

// CheckLoginData 检查登录数据
func (r *repo) CheckLoginData(c *gin.Context, p *models.RequestAdminLogin) (np *models.ResponseAdminLoginRegister, err error) {
	sqlStr := `SELECT admin_id FROM manage__admin WHERE admin_name=? AND admin_pwd=?`
	var adminId string
	if err = r.db.Get(&adminId, sqlStr, p.AdminName, p.AdminPwd); err != nil {
		if err == sql.ErrNoRows {
			err = errAdminUserNotExists
			zaplog.L().Warn("invalid username or password", zaplog.Fields(c, p, err)...)
			resp.UnprovedParam(c, errAdminUserNotExists.Error())
			return
		}
		zaplog.L().Error("query user failed", zaplog.Fields(c, p, err)...)
		resp.ServerBusy(c)
		return
	}
	var groupIds []string
	sqlStr = `SELECT group_id FROM manage__group_admin WHERE admin_id=?`
	if err = r.db.Select(&groupIds, sqlStr, adminId); err != nil {
		zaplog.L().Error("query admin group ids failed", zaplog.Fields(c, p.AdminId, err)...)
		resp.ServerBusy(c)
		return
	}
	np = new(models.ResponseAdminLoginRegister)
	np.AdminId = adminId
	np.AdminName = p.AdminName
	np.GroupIds = groupIds
	return
}

// ListAdmin 数据库查询结果
func (r *repo) ListAdmin(c *gin.Context, p *models.RequestListAdmin) (data *models.ResponseListAdmin, err error) {
	data = new(models.ResponseListAdmin)
	records := make([]*models.AAdmin, 0, p.PageSize.Size)

	query := `SELECT a.admin_id, a.admin_name, a.create_time, a.last_update_time,
        b.admin_name AS create_by, c.admin_name AS last_update_by
    	FROM manage__admin a, manage__admin b, manage__admin c
		WHERE a.create_by=b.admin_id
		AND a.last_update_by=c.admin_id`
	var args []interface{}

	if p.AdminId != "" { // adminId 查询
		query += ` AND a.admin_id=?`
		args = append(args, p.AdminId)
	}
	if p.AdminName != "" { // adminName 查询
		query += ` AND a.admin_name LIKE CONCAT('%', '?', '%')`
		args = append(args, p.AdminName)
	}
	if p.TimeRange.TimeRangeStart != "" && p.TimeRange.TimeRangeEnd != "" { // 创建时间查询
		query += ` AND a.create_time BETWEEN ? AND ?`
		args = append(args, p.TimeRange.TimeRangeStart)
		args = append(args, p.TimeRange.TimeRangeEnd)
	}
	if p.CreateBy != "" { // 创建者查询
		query += ` AND a.create_by=?`
		args = append(args, p.CreateBy)
	}
	sqlStr := query + ` LIMIT ? OFFSET ?` // 分页
	args = append(args, p.PageSize.Size)
	args = append(args, (p.PageSize.Page-1)*p.PageSize.Size)
	if err = r.db.Select(&records, sqlStr, args...); err != nil {
		zaplog.L().Error("query admin failed", zaplog.Fields(c, args, err)...)
		resp.ServerBusy(c)
		return
	}
	var count int64
	sqlStr = fmt.Sprintf(`SELECT COUNT(*) FROM (%s) AS alias`, query)
	if err = r.db.Get(&count, sqlStr, args[:len(args)-2]...); err != nil {
		zaplog.L().Error("query admin user count failed", zaplog.Fields(c, nil, err)...)
		resp.ServerBusy(c)
		return
	}
	data.Records = records
	data.Count = count
	return
}
